59. 了解并使用库

      这个方法看起来不错,但它有三个缺点。首先,如果 n 是小的平方数,随机数序列会在相当短的时间内重复。第二个缺陷是,如果 n 不是 2 的幂,那么平均而言,一些数字将比其他数字更频繁地返回。如果 n 很大,这种效果会很明显。下面的程序有力地证明了这一点,它在一个精心选择的范围内生成 100 万个随机数,然后打印出有多少个数字落在范围的下半部分:

      如果 random 方法工作正常,程序将输出一个接近 50 万的数字,但是如果运行它,你将发现它输出一个接近 666666 的数字。随机方法生成的数字中有三分之二落在其范围的下半部分!

      random 方法的第三个缺陷是,在极少数情况下会返回超出指定范围的数字,这是灾难性的结果。这是因为该方法试图通过调用 将 rnd.nextInt() 返回的值映射到非负整数。如果 nextInt() 返回整数。、Math.abs 也将返回整数。假设 n 不是 2 的幂,那么 Integer.MIN_VALUE 和求模运算符 将返回一个负数。几乎肯定的是,这会导致你的程序失败,并且这种失败可能难以重现。

      从 Java 7 开始,就不应该再使用 Random。在大多数情况下,选择的随机数生成器现在是 ThreadLocalRandom。 它能产生更高质量的随机数,而且速度非常快。在我的机器上,它比 Random 快 3.6 倍。对于 fork 连接池和并行流,使用 SplittableRandom。

      使用这些库的第二个好处是,你不必浪费时间为那些与你的工作无关的问题编写专门的解决方案。如果你像大多数程序员一样,那么你宁愿将时间花在应用程序上,而不是底层管道上。

      使用标准库的第三个优点是,随着时间的推移,它们的性能会不断提高,而你无需付出任何努力。由于许多人使用它们,而且它们是在行业标准基准中使用的,所以提供这些库的组织有很强的动机使它们运行得更快。多年来,许多 Java 平台库都被重新编写过,有时甚至是反复编写,从而带来了显著的性能改进。使用库的第四个好处是,随着时间的推移,它们往往会获得新功能。如果一个库丢失了一些东西,开发人员社区会将其公布于众,并且丢失的功能可能会在后续版本中添加。

      考虑到所有这些优点,使用库工具而不选择专门的实现似乎是合乎逻辑的,但许多程序员并不这样做。为什么不呢?也许他们不知道库的存在。在每个主要版本中,都会向库中添加许多特性,了解这些新增特性是值得的。 每次发布 Java 平台的主要版本时,都会发布一个描述其新特性的 web 页面。这些页面非常值得一读 [Java8-feat, Java9-feat]。为了强调这一点,假设你想编写一个程序来打印命令行中指定的 URL 的内容(这大致是 Linux curl 命令所做的)。在 Java 9 之前,这段代码有点乏味,但是在 Java 9 中,transferTo 方法被添加到 InputStream 中。这是一个使用这个新方法执行这项任务的完整程序:

      这些标准类库太庞大了,以致于不可能学完所有的文档 [Java9-api],但是 每个程序员都应该熟悉 java.lang、java.util 和 java.io 的基础知识及其子包。 其他库的知识可以根据需要获得。概述库中的工具超出了本条目的范围,这些工具多年来已经发展得非常庞大。

      其中有几个库值得一提。Collections 框架和 Streams 库(详见第 45 到 48 条)应该是每个程序员的基本工具包的一部分,java.util.concurrent 中的并发实用程序也应该是其中的一部分。这个包既包含高级的并发工具来简化多线程的编程任务,还包含低级别的并发基本类型,允许专家们自己编写更高级的并发抽象。 的高级部分,在第 80 条和第 81 条中讨论。

      总而言之,不要白费力气重新发明轮子。如果你需要做一些看起来相当常见的事情,那么库中可能已经有一个工具可以做你想做的事情。如果有,使用它;如果你不知道,检查一下。一般来说,库代码可能比你自己编写的代码更好,并且随着时间的推移可能会得到改进。这并不反映你作为一个程序员的能力。规模经济决定了库代码得到的关注要远远超过大多数开发人员所能承担的相同功能。